package com.boot.mall.product.web;

import com.boot.mall.product.entity.CategoryEntity;
import com.boot.mall.product.service.CategoryService;
import com.boot.mall.product.vo.Catalog2Vo;
import org.redisson.api.*;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.data.redis.core.StringRedisTemplate;
import org.springframework.stereotype.Controller;
import org.springframework.ui.Model;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.PathVariable;
import org.springframework.web.bind.annotation.ResponseBody;

import java.util.List;
import java.util.Map;
import java.util.UUID;
import java.util.concurrent.TimeUnit;

@Controller
public class IndexController {

    @Autowired
    private CategoryService categoryService;
    @Autowired
    private RedissonClient redissonClient;
    @Autowired
    private StringRedisTemplate stringRedisTemplate;

    @GetMapping({"/", "index.html"})
    public String getIndex(Model model) {
        //获取所有的一级分类
        List<CategoryEntity> catagories = categoryService.getLevel1Catagories();
        model.addAttribute("catagories", catagories);
        return "index";
    }

    @GetMapping("index/json/catalog.json")
    @ResponseBody
    public Map<String, List<Catalog2Vo>> getCategoryMap() {
        return categoryService.getCatalogJson();
    }

    @GetMapping("/hello")
    @ResponseBody
    public String hello(){
        //做一个简单的分布式可重入锁，只要锁的名字一样，那就是同一把伞
        RLock myLock = redissonClient.getLock("myLock");
        //加锁，此处是阻塞式等待，默认的加锁时间是30秒
        //  lock();
        //  Redisson实现两个优点
        //      1.看门狗机制：锁的自动续期，保证在超长业务处理中时加的锁不会因为过期被自动删除
        //      2.加锁的业务完成之后，就不会再给锁自动续期，即使不手动解锁，锁也会在30秒后自动删除
        //  lock(leaseTime,TimeUnit);
        //      设置锁的自动过期时间，注意设置锁的超时时间一定要大于处理业务时间，不存在看门狗机制
        //      如果设置了超时时间，就给Redis发送脚本进行占锁，默认超时就是我们指定的时间
        //  只要占锁成功，就会启动一个定时任务，重新给锁设置过期时间，新的过期时间就是看门狗的过期时间
        //      什么时候执行定时任务？在看门狗时间的三分之一后执行。也就是每隔10s续期一次，续期为30秒。
        //  最佳实战：
        //  1.推荐使用：myLock.lock(10, TimeUnit.SECONDS)，可以省略续期操作，需要手动解锁。
//        myLock.lock();
        myLock.lock(10, TimeUnit.SECONDS);
        System.out.println(Thread.currentThread().getName() + "加锁成功...");
        try {
            System.out.println(Thread.currentThread().getName() + "正在执行业务中...");
            Thread.sleep(20000);
        }catch (Exception e){

        }finally {
            //解锁
            System.out.println(Thread.currentThread().getName() + "将分布式锁给释放了...");
            myLock.unlock();
        }
        //执行业务
        return "hello";
    }

    /*------------------------读锁和写锁------------------------*/
    @GetMapping("/write")
    @ResponseBody
    public String writeValue(){
        //创建读写锁
        RReadWriteLock lock = redissonClient.getReadWriteLock("rw-lock");
        //通过读写锁创建写锁
        RLock writeLock = lock.writeLock();
        String uuid = "";
        //加锁
        writeLock.lock();
        System.out.println(Thread.currentThread().getName() + "加写锁成功...");
        try {
            //业务处理
            uuid = UUID.randomUUID().toString();
            stringRedisTemplate.opsForValue().set("value",uuid);
            Thread.sleep(10000);    //休眠
        }catch (Exception e){

        }finally {
            //释放锁
            writeLock.unlock();
            System.out.println(Thread.currentThread().getName() + "已经释放读锁...数据可被读取...");
        }
        return uuid;
    }

    @GetMapping("/read")
    @ResponseBody
    public String readValue(){
        //创建读写锁
        RReadWriteLock lock = redissonClient.getReadWriteLock("rw-lock");
        //通过读写锁创建读锁
        RLock rLock = lock.readLock();
        String uuid = "";
        //加锁
        rLock.lock();
        try {
            uuid = stringRedisTemplate.opsForValue().get("value");
        }finally {
            //解锁
            rLock.unlock();
        }
        return uuid;
    }
    /*---------------------读锁和写锁End------------------------*/

    /*------------------------信号量------------------------*/
    @GetMapping("park")
    @ResponseBody
    public String park(){
        //创建信号量
        RSemaphore park = redissonClient.getSemaphore("park");
        //尝试获取一个信号量，当信号量存在时返回true，否则返回false
        boolean acquire = park.tryAcquire();
        if (acquire){
            System.out.println("信号量获取成功！可以执行业务...");
            return "ok";
        }else {
            return "网络拥堵，请稍后重试！";
        }
    }

    @GetMapping("go")
    @ResponseBody
    public String go(){
        RSemaphore park = redissonClient.getSemaphore("park");
        //释放信号量
        park.release();
        return "ok";
    }
    /*------------------------信号量End------------------------*/
    /*------------------------闭锁------------------------*/
    @GetMapping("/lock")
    @ResponseBody
    public String lockDoor() throws InterruptedException {
        //创建一个闭锁
        RCountDownLatch lockDoor = redissonClient.getCountDownLatch("lockDoor");
        //设置闭锁等待的次数
        lockDoor.trySetCount(4);
        //设置闭锁阻塞
        lockDoor.await();
        return "闭锁已释放...";
    }

    @GetMapping("/back/{id}")
    @ResponseBody
    public String goBack(@PathVariable("id") String id){
        //获取闭锁
        RCountDownLatch lockDoor = redissonClient.getCountDownLatch("lockDoor");
        //闭锁等待次数减一
        lockDoor.countDown();
        return id + "号已释放..";
    }

}
